package com.xzy.demo.limiting.c;

import com.xzy.demo.limiting.Limiter;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.UUID;

/**
 * 流量限制器_基于令牌桶算法（不考虑并发、事务）
 *
 * @author xzy
 * @date 2021/10/12 15:13
 */
@Slf4j
@AllArgsConstructor
public class LimiterC implements Limiter {
    /**
     * 访问次数限制
     */
    public final long limit;

    /**
     * 访问时间限制（毫秒）
     */
    public final long expire;

    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 添加令牌
     *
     * @param uuid - 标识接口的UUID
     */
    public void push(String uuid) throws InterruptedException {
        while (true) {
            Thread.sleep(expire / limit);
            String token = UUID.randomUUID().toString();
            redisTemplate.opsForList().leftPush(uuid, token);
            log.debug("新增令牌：{}", token);
        }
    }

    /**
     * 是否限制访问
     *
     * @param uuid - 基于接口、用户信息生成的唯一值
     * @return - false：放行访问 true：限制访问
     */
    @Override
    public boolean isLimit(String uuid) {
        Long stockCount = redisTemplate.opsForList().size(uuid);
        if (stockCount == null || stockCount == 0) {
            log.debug("限制访问：无法获取访问令牌");
            return true;
        }

        String token = (String) redisTemplate.opsForList().rightPop(uuid);
        log.debug("允许访问: 访问令牌剩余{}，获取访问令牌{}", stockCount, token);
        return false;
    }
}
